package com.open.rbac.service.sys.impl;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.open.center.common.constant.OpenConstant;
import com.open.center.common.constant.OpenField;
import com.open.center.common.exception.OpenException;
import com.open.center.common.util.CopyUtils;
import com.open.rbac.model.dto.sys.OrganizationDto;
import com.open.rbac.model.dto.sys.OrganizationUserRelDto;
import com.open.rbac.model.entity.sys.Organization;
import com.open.rbac.model.vo.sys.OrganizationVo;
import com.open.rbac.service.sys.IOrganizationService;
import com.open.rbac.service.sys.IOrganizationUserRelService;
import com.open.rbac.service.sys.repository.OrganizationRepository;

/**
 * 组织机构
 *
 * @author Riche's
 * @since 2022/8/24
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class OrganizationServiceImpl implements IOrganizationService {

    private final OrganizationRepository organizationRepository;

    private final IOrganizationUserRelService organizationUserRelService;

    @Override
    public OrganizationDto create(OrganizationVo organizationVo) {
        Organization organization = CopyUtils.copy(organizationVo, Organization.class);
        List<Organization> organizations = organizationRepository.findByCode(organization.getCode());
        if (!CollectionUtils.isEmpty(organizations)) {
            throw new OpenException(String.format("组织机构Code[%s, %s]已存在",
                organizations.getFirst().getCode(), organizations.getFirst().getName()));
        }
        if (!StringUtils.hasText(organization.getParentId())) {
            organization.setParentId("0");
        }
        if (organization.getSort() == null) {
            organization.setSort(1);
        }
        organizationRepository.save(organization);
        return CopyUtils.copy(organization, OrganizationDto.class);
    }

    @Override
    public OrganizationDto update(OrganizationVo organizationVo) {
        Optional<Organization> optional = organizationRepository.findById(organizationVo.getId());
        return optional.map(o -> {
            CopyUtils.copyNonNull(organizationVo, o);
            organizationRepository.save(o);
            return CopyUtils.copy(o, OrganizationDto.class);
        }).orElse(null);
    }

    @Override
    public OrganizationDto findById(String id) {
        Optional<Organization> optional = organizationRepository.findById(id);
        return optional.map(org -> CopyUtils.copy(org, OrganizationDto.class)).orElse(null);
    }

    @Override
    public void deleteById(String id) {
        Optional<Organization> optional = organizationRepository.findById(id);
        optional.ifPresent(o -> {
            List<Organization> organizations = organizationRepository.findByParentId(id);
            if (!CollectionUtils.isEmpty(organizations)) {
                log.error("{} 下含有子节点，不能直接删除", o.getName());
                throw new OpenException(o.getName() + " 下含有子节点，不能直接删除");
            }
            organizationUserRelService.deleteReallyByOrganizationId(id);
            organizationRepository.deleteById(id);
        });
    }

    @Override
    public void enable(String id) {
        organizationRepository.updateEnable(id, true);
    }

    @Override
    public void disable(String id) {
        organizationRepository.updateEnable(id, false);
    }

    @Override
    public List<OrganizationDto> tree(OrganizationVo organizationVo) {
        List<Organization> organizations = organizationRepository.findAll(example(organizationVo),
            Sort.by(Sort.Direction.ASC, OpenField.SORT.getId()));
        if (CollectionUtils.isEmpty(organizations)) {
            return List.of();
        }

        // 根据子节点向上递归查询其父节点ID列表
        List<Organization> allOrganizations = organizationRepository.findAll(Sort.by(Sort.Direction.ASC, OpenField.SORT.getId()));
        List<OrganizationDto> allOrganizationDtos = CopyUtils.listCopy(allOrganizations, OrganizationDto.class);

        Set<String> resultAllIdList = new HashSet<>();
        organizations.forEach(e ->
            // 递归查询所有父节点ID, 包括自己
            resultAllIdList.addAll(this.getAllParentIds(allOrganizationDtos, e.getId(), new HashSet<>()))
        );
        // 根据ID列表过滤
        List<OrganizationDto> resultList = allOrganizationDtos.stream()
            .filter(e -> resultAllIdList.contains(e.getId()))
            .toList();

        // 构建树形结构
        List<OrganizationDto> rootList = resultList.stream()
            .filter(dto -> OpenConstant.ROOT_ORGANIZATION_ID.equals(dto.getParentId()))
            .toList();
        rootList.forEach(dto -> this.handleTree(dto, resultList));
        return rootList;
    }

    private void handleTree(OrganizationDto parent, List<OrganizationDto> allList) {
        List<OrganizationDto> children = allList.stream()
            .filter(e -> {
                if (OpenConstant.ROOT_ORGANIZATION_ID.equals(parent.getId())) {
                    return false;
                }
                return e.getParentId().equals(parent.getId());
            })
            .toList();
        parent.setChildren(children);
        children.forEach(e -> handleTree(e, allList));
    }

    private Set<String> getAllParentIds(List<OrganizationDto> allDtoList, String childId,
                                        Set<String> filterDtoList) {
        OrganizationDto firstDto = allDtoList.stream()
            .filter(e -> e.getId().equals(childId))
            .findFirst().orElse(null);
        if (firstDto == null) {
            return filterDtoList;
        }
        if (OpenConstant.ROOT_ORGANIZATION_ID.equals(firstDto.getParentId())) {
            filterDtoList.add(firstDto.getId());
            return filterDtoList;
        }
        filterDtoList.add(firstDto.getId());
        filterDtoList.addAll(getAllParentIds(allDtoList, firstDto.getParentId(), filterDtoList));
        return filterDtoList;
    }


    public Example<Organization> example(OrganizationVo organizationVo) {
        Organization organization = new Organization();
        if (organizationVo != null) {
            organization.setCode(organizationVo.getCode());
            organization.setName(organizationVo.getName());
            organization.setEnable(organizationVo.getEnable());
        }
        ExampleMatcher matcher = ExampleMatcher.matching()
            .withMatcher("code", ExampleMatcher.GenericPropertyMatcher::contains)
            .withMatcher("name", ExampleMatcher.GenericPropertyMatcher::contains)
            .withMatcher("enable", ExampleMatcher.GenericPropertyMatcher::exact)
            .withIgnoreCase(true);
        return Example.of(organization, matcher);
    }

    @Override
    public List<String> listChildIdsById(String id) {
        List<Organization> list = organizationRepository.findAll();
        if (CollectionUtils.isEmpty(list)) {
            return List.of();
        }
        // K: parentId, V: List<Children>
        Map<String, List<Organization>> mapParentIdChildren = list.stream()
            .collect(Collectors.groupingBy(Organization::getParentId));

        List<String> childIds = new ArrayList<>();
        Queue<String> queue = new ArrayDeque<>();

        // 从指定的 id 开始
        childIds.add(id);
        queue.add(id);

        while (!queue.isEmpty()) {
            String currentParentId = queue.poll();
            List<Organization> children = mapParentIdChildren.get(currentParentId);
            if (!CollectionUtils.isEmpty(children)) {
                for (Organization child : children) {
                    childIds.add(child.getId());
                    queue.add(child.getId());
                }
            }
        }
        return childIds;
    }

    @Override
    public Map<String, List<OrganizationDto>> mapByUserIds(List<String> userIds) {
        List<OrganizationUserRelDto> relDtos = organizationUserRelService.listByUserIds(userIds);
        if (CollectionUtils.isEmpty(relDtos)) {
            return Map.of();
        }
        List<String> ids = relDtos.stream().map(OrganizationUserRelDto::getOrganizationId).distinct().toList();
        List<OrganizationDto> organizationDtos = CopyUtils.listCopy(organizationRepository.findByIds(ids), OrganizationDto.class);
        Map<String, OrganizationDto> mapOrganizationDto = organizationDtos.stream()
                    .collect(Collectors.toMap(OrganizationDto::getId, Function.identity()));
        return relDtos.stream().collect(Collectors.groupingBy(
            OrganizationUserRelDto::getUserId,
            Collectors.mapping(rel -> mapOrganizationDto.get(rel.getOrganizationId()),
                Collectors.filtering(Objects::nonNull, Collectors.toList())
            )
        ));
    }
}
